7.2 Midterm project - text adventure assignment


7.2 Midterm project - text adventure assignment

You are probably wondering what a text adventure is and how can you build one with Python?

Text adventure

Have you ever read a choose your own adventure (CYOA) novel? A CYOA book lets the reader decide what the hero does at critical points in in the book. The author writes all the different story lines and tells you which page to turn to when you pick a particular action?

What about role playing games? Have you ever played a text adventure video game or role playing game with your friends? In role playing, one of your friends is the Dungeon Master (DM). The DM describes a world to you and your “party” of adventurers, and you all decide what to do at each turn in the game. In a text adventure, a computer program acts as the Dungeon Master. And the computer describes the world with text rather than with voice (and hand waving ;).

Don’t worry you only need to plan out a “dungeon” with 5 turns. You do not have to create a Dungeon Master program for an entire campaign. The goal is for you to demonstrate all the concepts you learned in Runestone over the past few weeks. Do you remember how to use each of these Python expressions?

  • print — display text
  • input — retrieve user input text
  • = — variable assignment
  • for, in, break — loops and iteration
  • if, elif, else — conditional logic
  • ==, >, <, >=, <= — boolean conditions
  • [-1], [0] — indexing a sequence using integers
  • ['countdown', 3, 2.0, 1, 'launch', '!'] — create lists of objects
  • [:3], range(3) — slicing and generating sequences
  • import random — importing built-in Python modules

Colossal Cave Adventure

To help you see an example of a very old text adventure game, here are the opening lines in the very first text adventure ever created:

Example text adventure “turns”

WELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?

no

YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING. AROUND YOU IS A FOREST. A SMALL STREAM FLOWS OUT OF THE BUILDING AND DOWN A GULLY.
YOU HAVE WALKED UP A HILL, STILL IN THE FOREST THE ROAD NOW SLOPES BACK DOWN THE OTHER SIDE OF THE HILL. THERE IS A BUILDING IN THE DISTANCE.

east

YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING.

THERE ARE SOME KEYS ON THE GROUND HERE.

HERE IS A SHINY BRASS LAMP NEARBY.

THERE IS FOOD HERE.

. . .

Cave adventure tree diagram

Cave Adventure tree diagram

Cave adventure Python code snippet

To implement Colossal Cave Adventure in Python, you might have some Python code like this to set up the first room in your text adventure:

cave_adventure.py

# Room 0
room0 = "WELCOME TO ADVENTURE!!"
# Room 0 question
room0_question = "WOULD YOU LIKE INSTRUCTIONS? "
# room_answers is list of pairs of possible_answers and next_room_num
# an empty list means that room is unreachable from this room, e.g. room0 is unreachable from room0
room0_answers = [
    [],
    ["yes", "y", "yep", "si"],
    ["no", "nope", "nah", ""],
]

# Room 1 (instructions)
# Room 1 does not have a question because it automatically returns to start room question
room1 = "In each room, you can type commands like 'look', 'east', 'talk', 'eat', and 'fight'."
# If you specify an empty room#_question or room#_answers list, the game will return to the previous room immediately
room1_question = "Practice entering commands by saying 'look' here: "
room1_answers = ['look', 'l', 'lk', 'lok', 'examine']

# Room 2
room2 = "YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING. AROUND YOU IS A FOREST. ..."
# Question for Room 1, can be reused in other rooms
room2_question = "WHAT DO YOU WANT TO DO? "
room2_answers = [
    [],  # not possible to go back to room0 (welcome)
    [],  # not possible to go back to room1 (instructions)
    ["look", "l", "lk", "lok"],  #
    ["east", "e", "est"],
    ["west", "w", 'wes', "wst"],
    ["north", "n", "nrth", "nth"],
    ["south", "s", "sth"],
]

# [description, question] pairs for each room
rooms = [
    [room0, room0_question],
    [room1, room1_question],
    [room2, room2_question],
]
# room_answers could be combined to the rooms lists, to reduce lines of code
# rooms would be [description, question, answers]
all_room_answers = [
    room0_answers,
    room1_answers,
    room2_answers,
]

# Start in room0 (first pair of strings in the list of rooms)
current_room_num = 0  #
################################################
# Start room 0 (welcome)

# the first half of the room [description, question] pair is at index `[0]`
description = rooms[current_room_num][0]
# the second half of the room [description, question] pair is at index `[1]`
question = rooms[current_room_num][1]
print(description)
answer = input(question)
next_room_answers = all_room_answers[current_room_num]
for i in range(len(next_room_answers)):
    print("DEBUG i next_room_answers[i]: ", i, next_room_answers[i])
    if answer in next_room_answers[i]:  # is player answer str in list of possible answers that send them to room i
        print("DEBUG i answer next_room_answers[i]: ", i, next_room_answers[i])
        current_room_num = i  # next_room_answers[i][1]  # this will be the next room
        break  # , game will remain in current room
print("DEBUG: current_room_num", current_room_num)
# End room 0 (welcome)
################################################

################################################
# Next room (may be room 0 (welcome), 1 (instructions), or 2 ("ROAD")

# the first half of the room [description, question] pair is at index `[0]`
description = rooms[current_room_num][0]
# the second half of the room [description, question] pair is at index `[1]`
question = rooms[current_room_num][1]
print(description)
answer = input(question)
# End next room
#################################################

Notice that I gave multiple possible ways for the user to say “yes.” Can you guess why I did not need to include variations for capitalizaiton of “yes”, such as “YES” and “Yes”? Perhaps you can think of better data structures to store your text content. Could you use a dictionary (dict)? For now, feel free to “hard code” most of the data and use whatever data types you are comfortable with.

Once you’ve written the data (text that you want to display), you can print out the start message and check for the user input. And you will need some conditional expressions to decide what happens in the next room. Your adventure doesn’t have to have rooms. It can be outdoors or even in outer space! But you just need a way to name the places in your game, so you don’t get confused about what to print when. This is where a pencil drawing of your text adventure map can help a lot. The best kind of diagram for a game like this is a “tree” diagram, like you might sometimes see in an Org Chart at a business.

Here are some lines that might be run right after the variable definitions above. These lines would start the Colosal Cave Adventure by printing a room description and a question, asking for user input.

print(start)
answer = input(start_question)
for i in range(len(start_answers)):
    if next_room

YOUR text adventure

A lot of great programmers (like Willie Crowthers) got their start by building text adventures. Your mission, if you chose to accept it, is to create a text adventure. Your Python program only needs to be able to print things to the terminal screen and take input from the user.

The fun part is creating the rooms and levels of the game - the text in your text adventure. But if this assignment is too fun for you, and you’d rather build something more serious, think about how your program could be a virtual assistant. Text-based programs like this are the brains behind popular virtual assistants like MyCroft.AI, You.com, and Qary.ai. So feel free to turn your game into a “virtual assistant” or a simple chatbot.

The only requirements for this exercise are that your program:

  • has 5 or more “rooms” (states)
  • understands 4 or more commands (keywords)
  • allows the user to visit all rooms
  • allows the user to reach the end - your program should eventually halt or exit, but NOT HCF ;-)

Programming Roadmap

For every programming project you’ll want to do these ten things:

  1. Ideation - think of an idea for your program
  2. Sketch a wireframe - think about what your program looks like when it runs
  3. Write pseudo code for a small piece of the program
  4. Write Python code for a small piece of the program
  5. Test the program yourself (developer testing)
  6. Go to step 4 to fix bugs until your program works
  7. Go to step 3 to add a feature that makes your program a little better… until the program is minimally useful
  8. Release or share your program

1. Ideation

Dream up a story you’d like to tell with your game. Is the player playing the role of a human, animal, monster, fantasy character, supernatural being, or alien? Is the world present day Earth, a future dystopia, or “A long time ago in a galaxy far far away.” Feel free to sketch your ideas or draw mind maps, talk it over with friends or your fellow students. The best ideas usually come out of the “collective consciousness” of several people working together.

The opening line is really important. It’s like the “pitch” for a Silicon Valley software startup. How will your adventure start? And how will your adventure end? Keep your story short, for now, you can expand it later, once you have something built. Maybe your player just needs to find their way out of a haunted house, or room, like a “Scooby Do” cartoon, or IRL Escape Room, a James Bond prison cell, or a Star Wars garbage compactor.

The hardest part is thinking up a name for your program. You can change it later, but make sure it’s short and sweet so your future self can easily type it to find it on your computer. Create a directory on your computer to hold your program.

I keep all my programs in a directory called code in my user directory. So on my computer my “Cloak and Dagger” adventure might be in a path like this:

/home/hobs/code/spy_adventure/

2. Wireframe

Sketch out your idea on paper or in a drawing program or text editor on your computer. I like to use a markdown file called (ideas.md) in the docs/ directory of the folder where I’m sorting on my project So to build your text adventure you’ll start with a “wire frame” or design so you can plan out your story.

Just like in life, it’s easier for the machine to talk than it is to listen. The hard part of building a text adventure is dealing with the text that the user inputs into the console of your game.

I keep all my documentation and notes in a directory called docs in my project. So my ideas would be in a file path like this:

/home/hobs/code/spy_adventure/docs/ideas.md

3. Pseudocode

Next you need to think about what your program needs to do. You can think of pseudo code as an English recipe or set of instructions you would tell a human for how to do what you want them to do. Only this human is trapped in a computer and can only manipulate data from input. And it can only display text or graphics on the screen of the computer. Pseudocode helps you plan your program. You can see examples in Chapter 1, section 1.2 in the FOPP textbook.

So open your docs/ideas.md file and add some pseudocode to help you plan the algorithms or functions you need for your text adventure game.

  • What are the inputs to your program?
  • What are the outputs to your program?
  • What are the things the computer needs to do to the inputs to decide what to output?
  • Can you think of some functions that might be useful in helping your program create that output?

Open your ideation notes and wireframe description file in your IDE so you can add some pseudocode to it. Smart IDEs like Sublime Text let you find files with Ctrl + P. So tomorrow type Ctrl + P and then type “spyideasmd” and your IDE will go straight to your new game ideas file if it has those words in it. And in even the most basic IDE or text editor, you can usually use Ctrl + F or Ctrl + Shift + F to find words within any file. Here’s your cheat sheet for finding file paths in your IDE:

4. Python!!

Finally you can start writing some Python! Create a new file called app.py in your project directory. My full path looked like this: /home/hobs/code/spy_adventure/app.py.

Next you want to add some documentation or comments to the file. Having this documentation is a great way to “pick up where you left off” each time you open the file. Once you start to “think in Python,” you will discover that your ideas (step 1), wireframe (step 2), and pseudocode (step 3) will contain bits of Python code that you can copy and paste into your Python file. For now just paste some of your pseudocode as-is into a docstring or comment at the top of your app.py file. Use the keyword pass for now in all your function definitions.

Create a main function called app in this file. Make this a stub that doesn’t do anything useful. You can use the pass keyword to create stubs in Python:

def do_something():
    pass

Or if you prefer you can create a stub function by just returning None:

def your_awesome_python_function():
    return None

Your main app() function will be used to call several other functions in your program. All programs should be broken up into functions so that they are easy to debug and think about. So go ahead and create some stub functions for the algorithms or functions you dreamed up during ideation. I can think of at least three useful functions for a text adventure program:

  1. input: prompt the user for input and return that input
  2. game logic: decide which action the user wants to perform and
  3. output: display the text describing the state or “room” of your user’s adventure

Now, within your app() function call all your stub functions in the order you want them to run. They don’t need to do anything, you just want to get the structure of your code working.

5. Test

The key to active learning for anything you want to do in life is to

  1. try stuff that may not work (make mistakes)
  2. look for unexpected things that happen
  3. learn from your “mistakes”

If you don’t make mistakes, you don’t have anything to learn.

So you need to test your application to see if there are mistakes. This is how you can run your Python code:

python app.py

If you see error messages or unexpected output you will need to start debbugging it reread your code and see if you can find where it went wrong.

If don’t your code does what you intended then it is correct then your tests have “passed”. Whenever your code works the way you expect, you can skip step 6. (“Debug”) and move on to step 7 (adding a feature).

Once you’ve created a working game, copy the text from your test session in the terminal (console). Paste your game session into the discussion channel so your classmates can see. Make sure you interact with your game for at least 4 inputs (commands) so you can get full credit.

6. Debug

Debugging code is an important skill. You will get better and better at it over time. FOPP has an entire chapter

HINT:

There’s another way to run Python programs that can sometimes be more useful when you are debugging. Within the iPython interactive console (sometimes called a “REPL”) you can use the magic command %run to process your file with the python interpretter. First you need to launch the ipython interactive console:

ipython

Within the ipython console you

# In [1]
%run app.py

7. Add a feature

Output

You can start by creating a data structure to hold your room or scene descriptions. If you can’t think of a cool fantasy or SciFi world, you can use your home. The goal is to get familiar with the mechanics of creating a game. Your game just has to be fun enough that you don’t get bored while testing it.

Input

You might want to have a list of four or five commands or actions that your players can use no matter where they are. The commands “north”, “south”, “east”, “west” are good ones if you see your world as a rectangular grid in your mind. If your game is all about conversation and negotiation you might want to think about commands such as: say “hello”, ask “question”, tell “story”, tell “joke”, share “information”, give “advice”, say “goodbye”. If your game is about fighting you can add commands for “attack”, “defend”, “run” away, “duck”, “hide.” Keep the list of commands small at first. Until you learn about data-driven programs, you will need an if or elif statement for each command in each “room.” So if you have 5 rooms and 5 commands in each room, that’s 25 conditional expressions. Here’s the start of an example with 5 conditional expressions:

command = command.strip().lower()
room = room.strip().lower()
if command[0] == 'n' and room == 'shed':
    # call function for room to the north of the shed
elif command[0] == 'e' and room == 'shed':
    # call function for room to the East of the shed
elif command[0] == 'n' and room == 'garden':
    # call function for room to the North of the garden
elif ...
else ...

A nice feature is if your commands each have different first letters so they will be easier to recognize, even if your user only types one letter.

You can also have a world where you move from place to place within a conversation, like a chatbot.

Hint

Don’t ever name any of your functions or variables input. You should never use any builtin Python keyword to name any objects (variables) .

8. Share you program

For this exercise you can share your program with the rest of your class by uploading it to GitLab in the cs179 repository here: https://gitlab.com/tangibleai/community/cs179/-/tree/main/src/cs179/adventures

  1. save your Python code file (“py file”) using your last name. Mine would be “lane.py”
  2. Create a GitLab account
  3. Upload your lane.py file to the cs179/adventures/ directory by clicking the plus sign here
  4. Click upload to browse your file system and open the file on your computer to upload it to GitLab
  5. Hit the blue “Commit changes” button at the bottom of the page (this saves your file on GitLab)
  6. Click the blue “Create merge request” button.
  7. On the next page click the blue “Create merge request” button at the bottom of the page again.

References